#ifndef _SE_OPENGLBUFFERS_
#define _SE_OPENGLBUFFERS_

#include "Render/Buffers.h"

namespace SE
{
	class OpenGLVertexBuffer final : public VertexBuffer
	{
	private:
		U32 mRenderID;
		U32 mSize;
		BufferLayout mLayout;

	public:
		OpenGLVertexBuffer(const U32 size, void* buffer = nullptr);
		virtual ~OpenGLVertexBuffer();

		virtual void bind() const override;
		virtual void unbind() const override;
		
		virtual void setBuffer(void* buffer, const U32 size) override;

		virtual const BufferLayout& getLayout() const override;
		virtual void setLayout(const BufferLayout& layout) override;
	};

	class OpenGLIndexBuffer final : public IndexBuffer
	{
	private:
		U32 mRenderID;
		U32 mCount;

	public:
		OpenGLIndexBuffer(const U32 count, void* buffer = nullptr);
		virtual ~OpenGLIndexBuffer();

		virtual void bind() const;
		virtual void unbind() const;

		virtual void setBuffer(void* buffer, const U32 size) override;

		virtual U32 getCount() const;
	};

	class OpenGLVertexArray final : public VertexArray
	{
	private:
		U32 mRenderID;
		U32 mVertexBufferIndex = 0;
		Vector< Ref<VertexBuffer> > mVertexBuffers;
		Ref<IndexBuffer> mIndexBuffer;

	public:
		OpenGLVertexArray();
		virtual ~OpenGLVertexArray();

		virtual void bind() const override;
		virtual void unbind() const override;

		virtual void addVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) override;
		virtual void setIndexBuffer(const Ref<IndexBuffer>& indexBuffer) override;

		virtual const Vector< Ref<VertexBuffer> >& getVertexBuffers() const override;
		virtual const Ref<IndexBuffer>& getIndexBuffer() const override;
	};

	class OpenGLTextureBuffer final : public TextureBuffer
	{
	private:
		U32						mRenderID;
		Vector<U32>				mSize;
		RenderInternalFormat	mInternalFormat;
		RenderDataFormat		mDataFormat;
		WrapMode				mWrapMode;
		MinFilterMode			mMinFilterMode;
		MagFilterMode			mMagFilterMode;
		Boolean					mGenMipmaps;
		Boolean					mMultisample;

	public:
		OpenGLTextureBuffer(
			const U32					dimension,
			const U32*					size,
			const RenderInternalFormat	format,
			const WrapMode				wrapMode,
			const MinFilterMode			minFilterMode,
			const MagFilterMode			magFilterMode,
			const Boolean				genMipmaps,
			const Boolean				multisample);
		virtual ~OpenGLTextureBuffer();

		virtual U32 getRenderID() const override;
		virtual U32 getDimension() const override;
		virtual U32 getBufferSize(const U32 dimension) const override;

		virtual void setBuffer(void* buffer, U32* size, U32 dimension) override;

		virtual void bind(const U32 slot) const override;
	};

	class OpenGLRenderBuffer final : public RenderBuffer
	{
	private:
		U32						mRenderID;
		Vector<U32>				mSize;
		RenderInternalFormat	mInternalFormat;

	public:
		OpenGLRenderBuffer(
			const U32					dimension,
			const U32*					size,
			const RenderInternalFormat	format);
		virtual ~OpenGLRenderBuffer();

		virtual U32	getRenderID() const override;
		virtual U32	getDimension() const override;
		virtual U32	getBufferSize(const U32 dimension) const override;

		virtual void setBuffer(void* buffer, U32* size, U32 dimension) override;

		virtual void bind() override;
	};

	class OpenGLFrameBuffer final : public FrameBuffer
	{
	private:
		U32 mRenderID;
		U32 mWidth;
		U32 mHeight;
		Vector<AttachmentLayoutElement> mLayout;
		HashMap< AttachmentType, U32 > mLocationCaches;

	public:
		OpenGLFrameBuffer(const std::initializer_list<AttachmentLayoutElement>& layout);
		virtual ~OpenGLFrameBuffer();

		virtual void bind() const override;
		virtual void unbind() const override;

		virtual U32 getWidth() const override;
		virtual U32 getHeight() const override;
		virtual U32 getAttachmentID(const AttachmentType type) const override;

		virtual void setBuffer(const U32 width, const U32 height) override;
	};
} // !namespace SE

#endif // !_SE_OPENGLBUFFERS_